/**
 * ***************************************************************
 * JADE - Java Agent DEvelopment Framework is a framework to develop multi-agent
 * systems in compliance with the FIPA specifications. Copyright (C) 2000 CSELT
 * S.p.A.
 *
 * GNU Lesser General Public License
 *
 * This library is free software; you can redistribute it and/or modify it under
 * the terms of the GNU Lesser General Public License as published by the Free
 * Software Foundation, version 2.1 of the License.
 *
 * This library is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
 * details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this library; if not, write to the Free Software Foundation, Inc.,
 * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 * **************************************************************
 */
package agentsystem;

import static agentsystem.ProducerAgent.idString;
import jade.content.lang.Codec;
import jade.content.lang.sl.SLCodec;
import jade.content.onto.basic.Action;
import jade.core.AID;
import jade.core.Agent;
import jade.core.behaviours.Behaviour;
import jade.core.behaviours.TickerBehaviour;
import jade.domain.DFService;
import jade.domain.FIPAAgentManagement.DFAgentDescription;
import jade.domain.FIPAAgentManagement.ServiceDescription;
import jade.domain.FIPAException;
import jade.domain.FIPANames.Ontology;
import jade.domain.JADEAgentManagement.JADEManagementOntology;
import jade.domain.JADEAgentManagement.ShutdownPlatform;
import jade.lang.acl.ACLMessage;
import jade.lang.acl.MessageTemplate;
import jade.wrapper.AgentContainer;
import jade.wrapper.AgentController;
import java.util.ArrayList;

/**
 * This example shows a minimal agent that just prints "Hallo World!" and then
 * terminates.
 *
 * @author Giovanni Caire - TILAB
 */
public class GodAgent extends Agent {

    private int producerId = 0; 
    // The list of known producer agents
    private AID[] producerAgents;
    // The list of consumer agents
    private AID[] consumerAgents;
    
    
    protected void setup() {
        
        System.out.println("This is God! My name is " + getLocalName());

        Object[] args = getArguments();
        
        int producerCount = 7;
        int consumerCount = 100;
        int dimensionCount = 5;
        
        //create Consumer & Producer Agents according to args[]
        if(args != null && args.length == 3){
            
            producerCount = Integer.parseInt(args[0].toString());
            consumerCount = Integer.parseInt(args[1].toString());
            dimensionCount = Integer.parseInt(args[2].toString());   
        }        
        
        System.out.println("Configuration: producerCount: "+producerCount+" | consumerCount: "+consumerCount+" | dimensionCount: "+dimensionCount);
        
        Product.setNumberOfProductProperties(dimensionCount);
        
        java.util.Random rand = new java.util.Random();
        rand.setSeed(1027639);
        
        
        
        for(int i = 0; i < producerCount; ++i){
            String name = "ProducerNo"+producerId ;
            AgentContainer c = (AgentContainer) getContainerController();
            try {
                AgentController a;
                a = c.createNewAgent( name, "agentsystem.ProducerAgent", null );
                a.start();
                //delay producer agents from each other
                Thread.sleep(rand.nextInt(1+1000/producerCount));
            }
            catch (Exception e){
                e.printStackTrace();
            }
            ++producerId;
        }
        for(int i = 0; i<consumerCount; ++i){
            String name = "ConsumerNo"+i ;
            AgentContainer c = (AgentContainer) getContainerController();
            try {
                AgentController a;
                a = c.createNewAgent( name, "agentsystem.ConsumerAgent", null );
                a.start();
                //delay producer agents from each other
                Thread.sleep(rand.nextInt(1+1000/consumerCount));
            }
            catch (Exception e){
                e.printStackTrace();
            }
        }
        
       this.addBehaviour(new CheckForApocalypse(this, 20000));
        
       // this.addBehaviour(new InspireCreativePeople(this, 1000));
        
        // Make this agent terminate
       // doDelete();
    }

    /**
     * Inner class CheckForApocalypse. This is the behaviour used by God
     * to terminate all agents if an equilibrium is reached - in our case
     * that means that if all Producers produce very similar products we stop 
     * our simulation.
     */
    private class CheckForApocalypse extends TickerBehaviour {

        public CheckForApocalypse(Agent a, long period) {
            super(a, period);
        }

        @Override
        protected void onTick() {
            // Update the list of producer agents
            DFAgentDescription template = new DFAgentDescription();
            ServiceDescription sd = new ServiceDescription();
            sd.setType("coke-producer");
            template.addServices(sd);
            try {
                DFAgentDescription[] result = DFService.search(myAgent, template);
                //System.out.println("Found the following producer agents:");
                producerAgents = new AID[result.length];
                for (int i = 0; i < result.length; ++i) {
                    producerAgents[i] = result[i].getName();
                    //System.out.println(producerAgents[i].getName());
                }
            } catch (FIPAException fe) {
                fe.printStackTrace();
            }
            
            DFAgentDescription consumertemplate = new DFAgentDescription();
            ServiceDescription consumersd = new ServiceDescription();
            sd.setType("coke-consumer");
            template.addServices(consumersd);
            try {
                DFAgentDescription[] result = DFService.search(myAgent, consumertemplate);
                //System.out.println("Found the following producer agents:");
                consumerAgents = new AID[result.length];
                for (int i = 0; i < result.length; ++i) {
                    consumerAgents[i] = result[i].getName();
                    //System.out.println(producerAgents[i].getName());
                }
            } catch (FIPAException fe) {
                fe.printStackTrace();
            }
            

            // Perform the request
            myAgent.addBehaviour(new GodAgent.RequestPerformer());
        }
    }

    private class RequestPerformer extends Behaviour {

        private int repliesCnt = 0; // The counter of replies from producer agents
        private MessageTemplate mt; // The template to receive replies
        private int step = 0;
        ArrayList<Product> allProducts = new ArrayList();
        ArrayList<Product> allTastes = new ArrayList();
        private double maxDistance = 0;        
        public void action() {
            switch (step) {
                case 0:
                    // Send the cfp to all producers
                    ACLMessage cfp = new ACLMessage(ACLMessage.CFP);
                    for (int i = 0; i < producerAgents.length; ++i) {
                        cfp.addReceiver(producerAgents[i]);
                    }
                    cfp.setContent(idString+"What is your product?"); //god acts as a producer
                    cfp.setConversationId("buying-coke");
                    cfp.setReplyWith("cfp" + System.currentTimeMillis()); // Unique value
                    myAgent.send(cfp);
                    // Prepare the template to get proposals
                    mt = MessageTemplate.and(MessageTemplate.MatchConversationId("buying-coke"),
                            MessageTemplate.MatchInReplyTo(cfp.getReplyWith()));
                    step = 1;
                    break;
                case 1:
                    // Receive all proposals/refusals from seller agents
                    ACLMessage reply = myAgent.receive(mt);
                    if (reply != null) {
                        // Reply received
                        if (reply.getPerformative() == ACLMessage.PROPOSE) {
                            Product product= new Product(reply.getContent());
                            allProducts.add(product);
                        }
                        repliesCnt++;
                        if (repliesCnt >= producerAgents.length) {
                            // We received all replies
                            step = 2;
                        }
                    } else {
                        block();
                    }
                    break;
                case 2:
                    // Send the cfp to all consumers
                    ACLMessage consumerCfp = new ACLMessage(ACLMessage.CFP);
                    for (int i = 0; i < consumerAgents.length; ++i) {
                        consumerCfp.addReceiver(consumerAgents[i]);
                    }
                    consumerCfp.setContent("What is your taste?");
                    consumerCfp.setConversationId("asking-taste");
                    consumerCfp.setReplyWith("cfp" + System.currentTimeMillis()); // Unique value
                    myAgent.send(consumerCfp);
                    // Prepare the template to get proposals
                    mt = MessageTemplate.and(MessageTemplate.MatchConversationId("asking-taste"),
                            MessageTemplate.MatchInReplyTo(consumerCfp.getReplyWith()));
                    step = 3;
                    break;    
                    
                case 3:
                    // Receive all tastes from consumers
                    ACLMessage consumerReply = myAgent.receive(mt);
                    if (consumerReply != null) {
                        // Reply received
                        if (consumerReply.getPerformative() == ACLMessage.PROPOSE) {
                            Product product= new Product(consumerReply.getContent());
                            allTastes.add(product);
                        }
                        repliesCnt++;
                        if (repliesCnt >= consumerAgents.length) {
                            // We received all replies
                            step = 4;
                        }
                    } else {
                        block();
                    }
                    break;
                case 4:
                    /*
                    check the difference between the products and kill all agents
                    if all the products are similar
                    */
                    
                    for(int i = 0; i<allProducts.size(); ++i){
                        for(int j = 1; j<allProducts.size(); ++j){
                            if(i != j){
                                double dist  = allProducts.get(i).differenceTo(allProducts.get(j));
                                if(dist > maxDistance){
                                    maxDistance = dist;
                                }
                            }
                        }
                    }
                    
                    
                    int[] propertySums = new int[Product.numberOfProductProperties];
                    for(int i = 0; i<allTastes.size(); ++i){
                        for(int j = 0; j < propertySums.length; ++j){
                            propertySums[j] += allTastes.get(i).getProperty(j);
                        }
                    }
                    String ret="(";
        
                    for(int i=0;i<propertySums.length;i++)
                        ret=ret+propertySums[i]/allTastes.size()+";";

                    ret += ")"; 
                    System.out.println("MARKET-TASTE: "+ret);
                    
                    if(maxDistance <= 1){
                        //APOCALYPSE NOW
                        
                        
                        System.out.println("APOCALYPSE NOW");
                        System.out.println("All Products: ");
                        for(int i = 0; i<allProducts.size(); ++i){
                           System.out.println(allProducts.get(i).toString());
                        }
                        
                        
                        
                        
                        
                        jade.domain.JADEAgentManagement.KillAgent ka = new
                        jade.domain.JADEAgentManagement.KillAgent();
                        
                        Codec codec = new SLCodec();    
                        jade.content.onto.Ontology jmo = JADEManagementOntology.getInstance();
                        getContentManager().registerLanguage(codec);
                        getContentManager().registerOntology(jmo);
                        ACLMessage msg = new ACLMessage(ACLMessage.REQUEST);
                        msg.addReceiver(getAMS());
                        msg.setLanguage(codec.getName());
                        msg.setOntology(jmo.getName());
                        try {
                            getContentManager().fillContent(msg, new Action(getAID(), new ShutdownPlatform()));
                            send(msg);
                        }
                        catch (Exception e) {}

                        
                        doDelete();
                    } else{
                        System.out.println("Apocalypse failed: max Distance = " + maxDistance);
                        
                        step = 5;
                    }
                    break;
                
            }
        }

        public boolean done() {
            if (step == 2 && allProducts.isEmpty()) {
                System.out.println("Apocalypse failed: no producers found");
            }
            return ((step == 2 && allProducts.isEmpty()) || step == 5);
        }
    }  // End of inner class RequestPerformer
    
    protected void takeDown() {
        System.out.println("GOD committing suicide OoOoO");
    }

}
